<?php
/**
 * 框架验证器基类
 * @copyright Copyright(c) 2020 WillPHP
 * @author DaSongzi <24203741@qq.com/113344.com>
 * @version 2.0
 * @since 2020-09-10
 */
namespace wiphp;
//======验证规则说明=======
//1.require：不能为空
//2.strname：用户名格式
//3.strpwd：密码格式
//4.int：数字
//5.email：邮箱
//6.url：url地址
//7.phone：手机号
//8.unique：数据库中是否唯一
//9.captcha：验证码
//10.字段名： 与已有字段名相同
//11.自定义规则：自定义验证
abstract class Validate {
	protected $table = ''; //表名
	protected $pk = 'id'; //主键
	protected $rule = []; //验证字段 [字段名=>验证规则|验证规则]
	protected $msg = []; //验证提示[字段名=>提示1|提示2] 
	protected $scenes = []; //场景设置 ['login'=>['username','password']]
	protected $now_scene = '*'; //当前场景 *验证所有
	protected $error = ''; //错误信息	
	protected $tokenerror = 'Token validation failed'; //Token错误信息	
	protected $data = []; //要检测的数据
	//保存设置
	protected $auto = []; //自动完成设置[字段名=>函数or值]
	protected $infields = ''; //限制字段
	protected $nofields  = ''; //去除字段	
	//初始化场景
	public function __construct($scene = '*') {
		$this->now_scene = in_array($scene, array_keys($this->scenes))? $scene : '*';
	}
	private function _parse_auto($field, $fn) {
		$data = $this->data;
		if (is_numeric($fn)) {
			return $fn;
		}elseif (function_exists($fn)) {
			$arg = isset($data[$field])? $data[$field] : '';
			return $fn($arg);
		} else {
			return isset($data[$fn])? $data[$fn] : $fn;
		}
	}
	//获取自动完成
	protected function _auto_data() {		
		$auto = [];
		foreach ($this->auto as $field => $fn) {
			if (strpos($field, '@')) {
				list($fd, $scene) = explode('@', $field);				
				if ($scene == $this->now_scene) {
					$auto[$fd] = $this->_parse_auto($fd, $fn);	
				}	
			} else {
				$auto[$field] = $this->_parse_auto($field, $fn);	
			}		
		}
		return $auto;
	}
	//去除字段
	protected function _no_field($data) {		
		if (!empty($this->nofields)) {
			if (strpos($this->nofields, ',')) {
				$fields = explode(',', $this->nofields);
			} else {
				$fields = [$this->nofields];
			}
			$fields = array_merge($fields, ['ajax','token']);
			foreach ($fields as $k) {
				if (isset($data[$k])) unset($data[$k]);
			}
		}
		return $data;
	}
	//限制字段
	protected function _in_field($data) {
		if (!empty($this->infields)) {
			if (strpos($this->infields, ',')) {
				$fields = explode(',', $this->infields);
			} else {
				$fields = [$this->infields];
			}
			$in = array();
			foreach ($fields as $k) {
				if (isset($data[$k])) $in[$k] = $data[$k];
			}
			return $in;
		} else {
			return $data;
		}
	}
	//获取过滤后的数据
	public function getData() {
		$auto = $this->_auto_data();		
		$data = array_merge($this->data, $auto);		
		$data = $this->_no_field($data);
		$data = $this->_in_field($data);
		return $data;
	}	
	//验证token
	public function token($name = '') {		
		if (valid_token($name)) return true;
		$this->error = $this->tokenerror;
		return false;	
	}
	//验证数据
	public function check($data = []) {
		$this->data = array_merge($this->data, $data);
		if (empty($this->data)) return false;		
		if (!empty($this->rule)) {	
			foreach ($this->rule as $k => $v) {				
				if (isset($this->data[$k])) {	
					if ($this->now_scene == '*' || in_array($k, $this->scenes[$this->now_scene])) {
						$chk = $this->check_field($k, $this->data[$k]);
						if ($chk['status'] == 0) {
							$this->error = $chk['info'];
							return false;
						}
					}
				}
			}
		}
		return true;
	}	
	//获取错误信息
	public function getError() {
		return $this->error;
	}
	//测试变量,值
	protected function check_field($field, $val) {
		$data = $this->data;
		$check = $this->rule[$field];	//验证方法
		$info = $this->msg[$field];  //验证错误提示
		if (strpos($check, '|')) {
			$check = explode('|', $check);
			$info = explode('|', $info);
		} else {
			$check = [$check];
			$info = [$info];
		}		
		$chk = array('status' => 1); //返回验证结果
		foreach ($check as $k => $act) {			
			$chk['info'] = $info[$k];			
			if (strpos($act, '@')) {
				list($fn, $scene) = explode('@', $act);		
				// || $this->now_scene == '*'
				if ($scene == $this->now_scene) {
					$method = '_check_'.$fn;
				}				
			} else {				
				$method = '_check_'.$act;				
			}			
			if ($act == 'require' && empty($val)) {
				$chk['status'] = 0;
				break;
			} elseif (method_exists($this, $method) && !empty($val)) {
				$chk['status'] = $this->$method($val, $field)? 1 : 0;
				if ($chk['status'] == 0) break;
			} elseif (isset($data[$act]) && $val != $data[$act])  {
				$chk['status'] = 0;
				break;
			}
		}
		return $chk;
	}	
	protected function _check_unique($val, $k = '') {
		$find = M($this->table)->where(array($k=>$val))->find($this->pk);
		return !$find? true : false;
	}
	protected function _check_strname($val, $k = '') {
		return preg_match('/^[A-Z0-9]{2,20}$/i',$val);
	}
	protected function _check_strpwd($val, $k = '') {
		return ctype_alnum($val);
	}
	protected function _check_int($val, $k = '') {
		return ctype_digit($val);
	}
	protected function _check_email($val, $k = '') {
		return preg_match('/([a-z0-9]*[-_.]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[.][a-z]{2,3}([.][a-z]{2})?/i', $val);
	}
	protected function _check_url($val, $k = '') {
		return preg_match('/^https?:\/\/(\w+\.)?[\w\-\.]+(\.\w+)+/',$val);
	}
	protected function _check_phone($val, $k = '') {
		return preg_match('/^1[3456789]\d{9}$/',$val);
	}
	protected function _check_captcha($val, $k = '') {
		if (isset($_SESSION[$k])) {
			return (md5($val) == $_SESSION[$k]);
		}
		return false;
	}
}